ICTSC2019 一次予選 問題解説: APIが飛ばないんですけど…

問題文


Webアプリケーションからドメインが異なるAPIにリクエストを発行する際には、クロスオリジンについて注意する必要があります。
CORS (Cross-Origin Resource Sharing) に関する以下の問いについて、それぞれ適切な選択肢を選んでください。

問1

https://example.com と同じOriginを選んで下さい。

問2

app.ictsc で動いているアプリケーションから api.ictsc へ以下のような fetch() を実行したところ、CORSのエラーで正常に動きませんでした。 api.ictsc に設定する必要があるHTTP response headerをすべて選んでください。

fetch({
  method: "POST",
  headers: {
    "Content-Type": "application/json"
  },
  "body": JSON.stringify(data)
})
  • Access-Control-Allow-Origin
  • Access-Control-Allow-Headers
  • Access-Control-Allow-Methods

問3

選択肢に示すHTTPメソッドのうち、いかなる場合においてもpreflight requestが行われるものを選んでください。

  • GET
  • POST
  • HEAD
  • DELETE

問4

preflight requestについて示した文章のうち、正しいものを全て選んでください。

  • リクエスト元のドメインとリクエスト先のドメインが同じ場合は、いかなる場合においてもpreflight requestは行われない。
  • クロスオリジンで独自HTTPメソッド TEST を発行するためには、Access-Control-Allow-Methods* を追加することで必ず正しく動く。
  • preflight requestに対する応答は、Access-Control-Allow-* ヘッダの内容が正しいHTTP responseであれば他の内容はなんでもよい。
  • Access-Control-Allow-Origin* を設定しておけば、他のヘッダが適切である限りいかなる場合でも動作する。

解説

問1

ポート番号、プロトコル(HTTP か HTTPS か)、ホストが一致するときのみ同一のOriginとなります。したがって https://example.com/hoge のみが正解です。

参考: https://developer.mozilla.org/ja/docs/Glossary/Origin

問2

問題文中の fetch では https://api.ictschttps://app.ictsc から POST リクエストが実行されます。これはホストが異なるため、異なるOriginへのリクエストになるので、応答の HTTP ヘッダに Access-Control-Allow-Origin が必要です。

リクエストには Content-Type ヘッダが含まれており、その値が application/json になっています。Content-Type が以下の3つの値以外のときは、実際のリクエストの前に preflight request が発行されます。

  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain

preflight request は OPTIONSメソッドで行われ、サーバ側の許可するメソッドやヘッダが応答の HTTP ヘッダ内の情報として返されます。問題文のように Content-Type: application/json のリクエストを送る場合は、サーバ側で prefilght request への応答の HTTP ヘッダに Access-Control-Allow-Headers: Content-Type と設定しておく必要があります。

一方で、メソッドがGET, HEAD, POST の場合は preflight request への応答の HTTP ヘッダに Access-Control-Allow-Methods をつける必要はありません。

したがって、設定するべきヘッダは Access-Control-Allow-OriginAccess-Control-Allow-Headers となります。

参考: https://developer.mozilla.org/ja/docs/Web/HTTP/CORS#Preflighted_requests

問3

preflight request の概要については問2の解説で説明したとおりです。メソッドが以下に挙げるものの場合は、必ず preflight request が発行されます。

  • PUT
  • DELETE
  • CONNECT
  • OPTIONS
  • TRACE
  • PATCH

したがって正解は DELETE となります。

問4

リクエスト元のドメインとリクエスト先のドメインが同じ場合は、いかなる場合においてもpreflight requestは行われない。

ドメインが同じであってもOriginが同じであるとは限りません。ポート番号やプロトコルが異なる場合は異なるOriginとなります。Originが異なる場合、特定の条件を満たせばpreflight requestが行われるため、この文章は間違っています。

クロスオリジンで独自HTTPメソッド TEST を発行するためには、Access-Control-Allow-Methods* を追加することで必ず正しく動く。

Access-Control-Allow-Methods: * と設定した場合の独自メソッドの動作は実装に依存しています。Ubuntu 18.0.4 上で Python3.6.8 の Bottle v0.12.17 によりHTTPサーバをhttp://localhost:8090, http://localhost:8080に立てて、前者から後者に JavaScript の fetch でリクエストを送って検証しました。Chromium 76.0.3809 で TESTリクエストを行ってみると成功しますが、FireFox 68.0.1 では失敗しました。 したがって、「必ず正しく動く」とするこの文章は間違っています。

参考: https://developer.mozilla.org/ja/docs/Web/HTTP/CORS/Errors/CORSMethodNotFound

preflight requestに対する応答は、Access-Control-Allow-* ヘッダの内容が正しいHTTP responseであれば他の内容はなんでもよい。

preflight request に対する応答のステータスコードが200番台でない場合、リクエストを送ることができません。上記と同様の検証環境で、preflight requestに対するHTTP responseのステータスコードを404にするとPOSTリクエストが飛ばないことを確かめられました。したがってこの文章は間違っています。

参考: https://fetch.spec.whatwg.org/#cors-preflight-fetch

Access-Control-Allow-Origin* を設定しておけば、他のヘッダが適切である限りいかなる場合でも動作する。

リクエストにCookieなどのリクエスト情報が含まれている場合、Access-Control-Allow-Origin: *というワイルドカードの指定ではなく、具体的なOriginの指定が必要です。したがって、「いかなる場合でも動作する」とするこの文章は間違っています。

参考: https://developer.mozilla.org/ja/docs/Web/HTTP/CORS#Requests_with_credentials

以上より、全ての文が間違っているので、何も選択しないのが正解です。